

#include "PriorityMemoryTable.h"


PriorityMemoryTable::PriorityMemoryTable(int parNumMaxSlot, MemoryWrapper* parMemory){

	//numMaxSlot = dimension / sizeof(PubPkt);
	numMaxSlot = parNumMaxSlot;
	numUsedSlot = 0;
	memory = parMemory;
	keyCounter = 0;

	timer = new Timer();


	PriorityTable prioTable;
	prioTable.count = 1;
	prioTable.window = 1000;
	prioTable.resettable = true;
	parameterTable.insert(make_pair("unknown", prioTable));

}

PriorityMemoryTable::~PriorityMemoryTable(){

}

void PriorityMemoryTable::updateTable(std::string type, int subscriptionCount, float window, bool resettable, int eventCount){

	boost::unordered_map<std::string, PriorityTable>::iterator it = parameterTable.find(type);
	if (it != parameterTable.end()){
		it->second.count = subscriptionCount;
		it->second.window = window;
		it->second.resettable = resettable;
	} else {
		PriorityTable priorityTable;
		priorityTable.count = subscriptionCount;
		priorityTable.window = window;
		priorityTable.resettable = resettable;
		parameterTable.insert(make_pair(type, priorityTable));
	}

}

void PriorityMemoryTable::resetTable(){

	oldParameterTable = parameterTable;
	oldClustersMap = clustersMap;

	parameterTable.clear();

	PriorityTable prioTable;
	prioTable.count = 1;
	prioTable.window = 1000;
	prioTable.resettable = true;
	parameterTable.insert(make_pair("unknown", prioTable));

	clustersMap.clear();

}


void PriorityMemoryTable::computeInsertion(PubPkt *pkt, std::string complexType){

	cout << "Memory manager: Priority table: start message insertion..." << endl;

	timer->start();

	TimeMs currentTime;
	timestamp_t currentTimestamp = currentTime.getTimeVal();

	if (numUsedSlot == numMaxSlot) {
		cout << "Memory manager: Priority table: capacity limit reached: need to remove a message." << endl;
		std::pair<timestamp_t, key_t> candidate;
		boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator pointerToCandidateQueue;
		//float lowerPriorityValue = -1;
		float lowerPriorityValue = 10000;
		if (oldClustersMap.size() != 0){
			bool foundRemovable = false;
			bool considerNonResettability = true;
			do {
				for (boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it = oldClustersMap.begin(); it != oldClustersMap.end(); it++){
					//cout << "itero..." << endl;
					//cout << it->first << endl;
					if (it->second.empty() ) continue;
					timestamp_t actualEventTimestamp = it->second.top().first;
					PriorityTable actualParameters = oldParameterTable[it->first];
					if (it->second.size()==1 && !actualParameters.resettable && considerNonResettability) continue;
					float actualPriorityValue;
					float occupancePercentage = (currentTimestamp - actualEventTimestamp)/ (int long)(actualParameters.window);
					if (occupancePercentage <= 1){
						actualPriorityValue = actualParameters.count*(1-occupancePercentage);
					} else {
						actualPriorityValue = (1-occupancePercentage) / actualParameters.count;
					}
					//if (lowerPriorityValue == -1 || actualPriorityValue < lowerPriorityValue){
					if (actualPriorityValue < lowerPriorityValue){
						lowerPriorityValue = actualPriorityValue;
						candidate = it->second.top();
						pointerToCandidateQueue = it;
						foundRemovable = true;
					}
				}
				if (!foundRemovable) { considerNonResettability = false; }
			} while (!foundRemovable);
			cout << "Memory manager: Priority table: remotion from old cluster " <<  pointerToCandidateQueue->first << ": key "<< candidate.second << endl;
			memory->remove(candidate);
			numUsedSlot--;
			pointerToCandidateQueue->second.pop();
			if (pointerToCandidateQueue->second.empty()){
				oldClustersMap.erase(pointerToCandidateQueue);
				//cout << "size: " << oldClustersMap.size() << endl;
				if (oldClustersMap.empty()){
					oldClustersMap.clear();
					oldParameterTable.clear();
					cout << "Memory manager: Priority table: old clusters completely resetted." << endl;
				}
			}
 		} else {
 			bool foundRemovable = false;
			bool considerNonResettability = true;
			do {
				for (boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it = clustersMap.begin(); it != clustersMap.end(); it++){
					if (it->second.empty()) continue;
					timestamp_t actualEventTimestamp = it->second.top().first;
					PriorityTable actualParameters = parameterTable[it->first];
					//cout << it->first <<": size "<< it->second.size() << " resettable " << actualParameters.resettable <<  endl;
					if (it->second.size()==1 && !actualParameters.resettable && considerNonResettability) continue;
					// CALCOLO VALORE PRIORITà
					float actualPriorityValue;
					//cout << (int long)(actualParameters.window) << endl;
					if (actualParameters.window == 0) {
						//lowerPriorityValue = 0;
						actualPriorityValue = 0;
						//candidate = it->second.top();
						//pointerToCandidateQueue = it;
						//break;
					} else {
						float occupancePercentage = (currentTimestamp - actualEventTimestamp)/ (int long)(actualParameters.window);
						if (occupancePercentage <= 1){
							actualPriorityValue = actualParameters.count*(1-occupancePercentage);
						} else {
							actualPriorityValue = (1-occupancePercentage) / actualParameters.count;
						}
					}
					if (lowerPriorityValue == -1 || actualPriorityValue < lowerPriorityValue){
						lowerPriorityValue = actualPriorityValue;
						candidate = it->second.top();
						pointerToCandidateQueue = it;
						foundRemovable = true;
					}
				}
				if (!foundRemovable) { considerNonResettability = false; }
			} while (!foundRemovable);
			//cout << "priorityValue: " << lowerPriorityValue << endl;
			cout << "Memory manager: Priority table: remotion from cluster " <<  pointerToCandidateQueue->first << ": key "<< candidate.second << endl;
			memory->remove(candidate);
			numUsedSlot--;
			pointerToCandidateQueue->second.pop();
			if (pointerToCandidateQueue->second.empty()){
				clustersMap.erase(pointerToCandidateQueue);
			}
		}
	}
	keyCounter++;
	timestamp_t eventTimestamp = pkt->getTimeStamp().getTimeVal();
	boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it3 = clustersMap.find(complexType);
	if (it3 != clustersMap.end()){
		it3->second.push(make_pair(eventTimestamp, keyCounter));
		cout << "Memory manager: Priority table: insertion into cluster " << it3->first << ": key " << keyCounter << endl;
	} else {
		boost::unordered_map<std::string, PriorityTable>::iterator it5 = parameterTable.find(complexType);
		if (it5 != parameterTable.end()){
			std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > queue;
			queue.push(make_pair(eventTimestamp, keyCounter));
			clustersMap.insert(make_pair(complexType, queue));
			cout << "Memory manager: Priority table: created cluster " << it5->first << endl;
			cout << "Memory manager: Priority table: insertion into cluster " << it5->first << ": key "<< keyCounter << endl;
		}
		else {
			boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it4 = clustersMap.find("unknown");
			if (it4 != clustersMap.end()){
				it4->second.push(make_pair(eventTimestamp, keyCounter));
				cout << "Memory manager: Priority table: insertion into cluster " << it4->first << ": key " << keyCounter << endl;
			} else {
				std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > queue;
				queue.push(make_pair(eventTimestamp, keyCounter));
				clustersMap.insert(make_pair("unknown", queue));
				cout << "Memory manager: Priority table: created cluster unknown" << endl;
				cout << "Memory manager: Priority table: insertion into cluster unknown: key "<< keyCounter << endl;
			}
		}

	}
	memory->insert(pkt, keyCounter);
	numUsedSlot++;

	timer->stop();
	cout << "Memory manager: Priority table: insertion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
}

void PriorityMemoryTable::computeExtraction(BackSubPkt *pkt, std::set<PubPkt*> &resultSet){
	cout << "Memory manager: Priority table: start messages extraction..." << endl;
//	std::set<PubPkt*> resultSet;
	memory->extract(pkt->getEventType(), pkt->getConstraintList(), pkt->getConstraintsNum(), pkt->getTimeStamp().getTimeVal(), pkt->getWindow(), pkt->getOccurrence(), resultSet);
	cout << "Memory manager: Priority table: extraction terminated." << endl;
//	return resultSet;

}
